home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / fax / src / faxd / faxServerApp.c++ < prev    next >
C/C++ Source or Header  |  1994-08-01  |  46KB  |  1,675 lines

  1. /*    $Header: /usr/people/sam/fax/faxd/RCS/faxServerApp.c++,v 1.147 1994/04/10 03:56:46 sam Rel $ */
  2. /*
  3.  * Copyright (c) 1990, 1991, 1992, 1993, 1994 Sam Leffler
  4.  * Copyright (c) 1991, 1992, 1993, 1994 Silicon Graphics, Inc.
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software and 
  7.  * its documentation for any purpose is hereby granted without fee, provided
  8.  * that (i) the above copyright notices and this permission notice appear in
  9.  * all copies of the software and related documentation, and (ii) the names of
  10.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  11.  * publicity relating to the software without the specific, prior written
  12.  * permission of Sam Leffler and Silicon Graphics.
  13.  * 
  14.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  15.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  16.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  17.  * 
  18.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  19.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  20.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  21.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  22.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  23.  * OF THIS SOFTWARE.
  24.  */
  25. #include <sys/types.h>
  26. #include <syslog.h>
  27. #include <dirent.h>
  28. #include <unistd.h>
  29. #include <sys/stat.h>
  30. #include <string.h>
  31. #include <ctype.h>
  32. #include <osfcn.h>
  33. #include <errno.h>
  34. #include <fcntl.h>
  35. #include <pwd.h>
  36. #include <math.h>
  37. #include <paths.h>
  38. #include <limits.h>
  39. #include <stdlib.h>
  40. #include <sys/wait.h>
  41. #include <sys/file.h>
  42.  
  43. #include <Dispatch/dispatcher.h>
  44.  
  45. #include "FaxServer.h"
  46. #include "FaxRecvInfo.h"
  47. #include "faxServerApp.h"
  48. #include "FaxMachineInfo.h"
  49. #include "config.h"
  50.  
  51. /*
  52.  * FAX Spooling and Command Agent.
  53.  */
  54.  
  55. const fxStr faxServerApp::fifoName    = FAX_FIFO;
  56. const fxStr faxServerApp::sendDir    = FAX_SENDDIR;
  57. const fxStr faxServerApp::recvDir    = FAX_RECVDIR;
  58. const fxStr faxServerApp::ps2faxCmd    = FAX_PS2FAX;
  59. const fxStr faxServerApp::notifyCmd    = FAX_NOTIFYCMD;
  60. const fxStr faxServerApp::faxRcvdCmd    = FAX_FAXRCVDCMD;
  61. const fxStr faxServerApp::pollRcvdCmd    = FAX_POLLRCVDCMD;
  62.  
  63. extern    void fxFatal(const char* va_alist ...);
  64.  
  65. faxServerApp::faxServerApp() : queueDir(FAX_SPOOLDIR)
  66. {
  67.     use2D = TRUE;
  68.     running = FALSE;
  69.     queue = 0;
  70.     currentTimeout = 0;
  71.     requeueInterval = FAX_REQUEUE;
  72.     fifo = -1;
  73.     devfifo = -1;
  74.     server = 0;
  75.     curreq = NULL;
  76.     openlog("FaxServer", LOG_PID, LOG_FAX);
  77. }
  78.  
  79. faxServerApp::~faxServerApp()
  80. {
  81.     if (fifo != -1) {
  82.     Dispatcher::instance().unlink(fifo);
  83.     ::close(fifo);
  84.     }
  85.     if (devfifo != -1) {
  86.     Dispatcher::instance().unlink(devfifo);
  87.     ::close(devfifo);
  88.     }
  89.     delete server;
  90.     closelog();
  91. }
  92.  
  93. void
  94. faxServerApp::initialize(int argc, char** argv)
  95. {
  96.     if (getuid() != 0)
  97.     fxFatal("The fax server must run with real uid root.\n");
  98.     uid_t euid = geteuid();
  99.     if (euid == 0) {
  100.     struct passwd* pwd = getpwnam(FAX_USER);
  101.     if (!pwd)
  102.         fxFatal("No fax user \"%s\" defined on your system!\n"
  103.         "This software is not installed properly!");
  104.     if (setegid(pwd->pw_gid) < 0)
  105.         fxFatal("Can not setup permissions (gid)");
  106.     if (seteuid(pwd->pw_uid) < 0)
  107.         fxFatal("Can not setup permissions (uid)");
  108.     } else {
  109.     struct passwd* pwd = getpwuid(euid);
  110.     if (!pwd)
  111.         fxFatal("Can not figure out the identity of uid %d", euid);
  112.     if (strcmp(pwd->pw_name, FAX_USER) && strcmp(pwd->pw_name, "uucp"))
  113.         fxFatal("The fax server must run as the fax user \"%s\".",
  114.         FAX_USER);
  115.     }
  116.  
  117.     appName = argv[0];
  118.     u_int l = appName.length();
  119.     appName = appName.tokenR(l, '/');
  120.  
  121.     extern int optind, opterr;
  122.     extern char* optarg;
  123.     int c;
  124.     opterr = 0;
  125.     fxBool debug = FALSE;
  126.     while ((c = getopt(argc, argv, "m:g:i:q:d1x")) != -1)
  127.     switch (c) {
  128.     case 'm':
  129.         device = optarg;
  130.         break;
  131.     case 'i':
  132.         requeueInterval = atoi(optarg);
  133.         break;
  134.     case 'q':
  135.         queueDir = optarg;
  136.         break;
  137.     case 'd':
  138.         debug = TRUE;
  139.         break;
  140.     case '1':
  141.         use2D = FALSE;
  142.         break;
  143.     case '?':
  144.         fxFatal("usage: %s"
  145.         " -m modem-device"
  146.         " [-g gettyspeed]"
  147.         " [-i requeue-interval]"
  148.         " [-q queue-directory]"
  149.         " [-d] [-1]"
  150.         , (char*) appName
  151.         );
  152.     }
  153.     if (device == "")
  154.     fxFatal("No modem device specified");
  155.     if (!debug)
  156.     detachFromTTY();
  157.  
  158.     serverPID = fxStr(getpid(), "%u");
  159.     /*
  160.      * Construct an identifier for the device special
  161.      * file by stripping a leading prefix (DEV_PREFIX)
  162.      * and converting all remaining '/'s to '_'s.  This
  163.      * is required for SVR4 systems which have their
  164.      * devices in subdirectories!
  165.      */
  166.     devID = device;
  167.     fxStr prefix(DEV_PREFIX);
  168.     u_int pl = prefix.length();
  169.     if (devID.length() > pl && devID.head(pl) == prefix)
  170.     devID.remove(0, pl);
  171.     while ((l = devID.next(0, '/')) < devID.length())
  172.     devID[l] = '_';
  173.     server = new FaxServer(this, device, devID);
  174.  
  175.     /*
  176.      * Requeue interval must be at least 2 seconds for
  177.      * calculations below.  Should probably force it to
  178.      * something much higher to avoid bad behaviour.
  179.      */
  180.     if (requeueInterval < 2)
  181.     requeueInterval = 2;
  182.  
  183.     if (::chdir((char*) queueDir) < 0)
  184.     fxFatal("%s: Can not change directory", (char*) queueDir);
  185.     fifo = openFIFO(fifoName, 0600, TRUE);
  186.     Dispatcher::instance().link(fifo, Dispatcher::ReadMask, this);
  187.     devfifo = openFIFO(fifoName | "." | devID, 0600, TRUE);
  188.     Dispatcher::instance().link(devfifo, Dispatcher::ReadMask, this);
  189.  
  190.     server->initialize(argc, argv);
  191. }
  192.  
  193. /*
  194.  * Break the association with the controlling
  195.  * tty if we can preserve it later with the
  196.  * POSIX O_NOCTTY mechanism.
  197.  */
  198. void
  199. faxServerApp::detachFromTTY()
  200. {
  201. #ifdef O_NOCTTY
  202.     int fd = ::open(_PATH_DEVNULL, O_RDWR);
  203.     dup2(fd, STDIN_FILENO);
  204.     dup2(fd, STDOUT_FILENO);
  205.     dup2(fd, STDERR_FILENO);
  206.     for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
  207.     if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
  208.         (void) ::close(fd);
  209.     switch (fork()) {
  210.     case 0:    break;        // child, continue
  211.     case -1:    _exit(1);    // error
  212.     default:    _exit(0);    // parent, terminate
  213.     }
  214.     (void) setsid();
  215. #endif
  216. }
  217.  
  218. void
  219. faxServerApp::open()
  220. {
  221.     traceServer("OPEN \"%s\"", (char*) device);
  222.     server->open();
  223.     scanQueueDirectory();
  224.     startTimer(time(0) + 5);        // poke scheduler in a bit
  225.     running = TRUE;
  226. }
  227.  
  228. void
  229. faxServerApp::close()
  230. {
  231.     if (running) {
  232.     traceServer("CLOSE \"%s\"", (char*) device);
  233.     stopTimer();
  234.     server->close();
  235.     }
  236.     running = FALSE;
  237. }
  238.  
  239. /*
  240.  * Scan the spool directory for queue files and enter them
  241.  * in the queue of outgoing jobs.  We do a flock on the
  242.  * queue directory while scanning to avoid conflict between
  243.  * servers (and clients).
  244.  *
  245.  * Note that what is done here is not right; we should
  246.  * read the qfiles and retrieve the time-to-send for each
  247.  * job (and also check the modem to use).  What we do
  248.  * instead just causes the server to reread the queue
  249.  * contents and do a single pass over it to reach a correct
  250.  * state.  This can result in our processing jobs in the
  251.  * wrong order.  Since this is done only once at server
  252.  * startup (which is assumed to be infrequent); we live
  253.  * with it.  When the queue management is rewritten this
  254.  * will need to be corrected.
  255.  */
  256. void
  257. faxServerApp::scanQueueDirectory()
  258. {
  259.     DIR* dir = opendir(sendDir);
  260.     if (dir) {
  261.     fxStr prefix(sendDir | "/");
  262.     (void) flock(dir->dd_fd, LOCK_SH);
  263.     time_t now = time(0);
  264.     Job** last = &queue;
  265.     for (dirent* dp = readdir(dir); dp; dp = readdir(dir)) {
  266.         /*
  267.          * XXX Beware, this code is carefully constructed to
  268.          * XXX avoid a bug in gcc 2.5.[78].
  269.          */
  270.         if (dp->d_name[0] == 'q') {
  271.         struct stat sb;
  272.         fxStr file(prefix | dp->d_name);
  273.         if (stat((char*)file, &sb) >= 0 &&
  274.           (sb.st_mode&S_IFMT) == S_IFREG) {
  275.             // NB: not right--should read tts from q file
  276.             Job* job = new Job(file, now);
  277.             job->next = 0;
  278.             *last = job;
  279.             last = &job->next;
  280.         }
  281.         }
  282.     }
  283.     (void) flock(dir->dd_fd, LOCK_UN);
  284.     closedir(dir);
  285.     } else
  286.     logError("Could not scan queue directory");
  287.     // XXX could do some intelligent queue processing
  288.     // (e.g. coalesce jobs to the same destination)
  289. }
  290.  
  291. /*
  292.  * Process the next job in the queue.  The queue file
  293.  * is locked to avoid conflict with other servers that
  294.  * are processing the same spool area.  The queue file
  295.  * associated with the job is read into an internal
  296.  * format that's sent off to the fax server thread.  When
  297.  * the fax server is done with the job (either sending the
  298.  * files or failing), the transmitted request is returned,
  299.  * triggering a call to notifyJobComplete.  Thus if
  300.  * we fail to handle the entry at the front of the queue
  301.  * we just simulate a server response by invoking the
  302.  * method directly.
  303.  */
  304. void
  305. faxServerApp::processJob(Job* job)
  306. {
  307.     int fd = ::open((char*) job->file, O_RDWR);
  308.     if (fd >= 0) {
  309.     time_t now = time(0);
  310.     if (flock(fd, LOCK_EX|LOCK_NB) >= 0) {
  311.         FaxRequest* req = readQFile(job->file, fd);
  312.         if (req) {
  313.         if (req->modem == MODEM_ANY || req->modem == devID) {
  314.             /*
  315.              * Job is for us, check kill time, then tts.
  316.              */
  317.             if (now < req->killtime) {
  318.             /*
  319.              * If it is time to send this job and the modem is
  320.              * available, proceeed.  Otherwise reset the tts
  321.              * and requeue the job.
  322.              */
  323.             if (req->tts <= now && server->modemReady()) {
  324.                 // NB: gets locked, open file descriptor
  325.                 processJob1(job, req);
  326.                 return;
  327.             }
  328.             if (req->tts > now) {
  329.                 /*
  330.                  * It is not time yet, just requeue the job.
  331.                  */
  332.                 traceQueue("SEND NOT READY: \"%s\" in %s",
  333.                 (char*) req->qfile, fmtTime(req->tts - now));
  334.                 job->tts = req->tts;
  335.             } else {
  336.                 /*
  337.                  * The modem is not ready.  Set the tts
  338.                  * so that the job will be processed
  339.                  * immediately when we are notified that the
  340.                  * modem is available for use.
  341.                  */
  342.                 job->tts = req->tts;
  343.             }
  344.             insertJob(new Job(job));
  345.             delete req;
  346.             } else
  347.             /*
  348.              * Job has expired, terminate it and notify user.
  349.              */
  350.             deleteRequest(Job::timedout, req, TRUE);
  351.         } else            // job is for another server
  352.             delete req;
  353.         } else
  354.         logError("Could not read q file for \"%s\"", (char*) job->file);
  355.     } else {
  356.         if (errno == EWOULDBLOCK) {
  357.         /*
  358.          * The job is either being scanned by another server or
  359.          * by a faxd.recv process.  Bump the tts a bit so that
  360.          * if this is the job at the head of the queue it'll
  361.          * get reprocessed soon.
  362.          */
  363.         job->tts = now + random() % 30;
  364.         insertJob(new Job(job));
  365.         } else
  366.         logError("Could not lock job file \"%s\": %m",
  367.             (char*) job->file);
  368.     }
  369.     ::close(fd);
  370.     } else {
  371.     // file might have been removed by another server
  372.     if (errno != ENOENT)
  373.         logError("Could not open job file \"%s\" (errno %d)",
  374.         (char*) job->file, errno);
  375.     }
  376. }
  377.  
  378. /*
  379.  * Check the remote machine info data to see if
  380.  * there is an outgoing job currently being processed.
  381.  * Note that we verify the specified job still exists
  382.  * to deal with jobs being removed from the queue w/o
  383.  * the associated info file being updated.  This is
  384.  * because it is very hard to figure out the correct
  385.  * info file to update when a job is removed by a user
  386.  */
  387. static fxBool
  388. destIsBusy(FaxMachineInfo& info, const fxStr& qfile)
  389. {
  390.     const fxStr& job = info.getJobInProgress();
  391.     if (job != "" && job != qfile) {
  392.     struct stat sb;
  393.     return (stat((char*) job, &sb) >= 0);
  394.     } else
  395.     return (FALSE);
  396. }
  397.  
  398. /*
  399.  * Calculate the time to retry a job after some unspecified
  400.  * problem.  This calculation is used when a retry time has'nt
  401.  * already been specified (e.g. on getting a busy signal the
  402.  * job is retried faster).
  403.  */
  404. time_t
  405. faxServerApp::requeueTTS(time_t now) const
  406. {
  407.     return (now + (requeueInterval>>1) + (random() % requeueInterval));
  408. }
  409.  
  410. /*
  411.  * Process a ``send job''.
  412.  *
  413.  * Check the remote machine registry for the destination
  414.  * machine's current call status and use that info to
  415.  * decide if the job should be started.
  416.  */
  417. void
  418. faxServerApp::processJob1(Job* job, FaxRequest* req)
  419. {
  420.     fxStr canon(server->canonicalizePhoneNumber(req->external));
  421.     if (canon != "") {
  422.     FaxMachineInfo info(canon, FALSE);
  423.     if (info.isBusy() || destIsBusy(info, req->qfile)) {
  424.         /*
  425.          * Another call/job is in progress to this machine,
  426.          * requeue this job until the current job is done.
  427.          */
  428.         fxStr notice;
  429.         time_t now = time(0);
  430.         if (info.isBusy()) {
  431.         job->tts = now + random() % 30;    // XXX maybe too agressive
  432.         notice = "Blocked by concurrent call";
  433.         if (notice != req->notice) {    // avoid frequent updates
  434.             req->notice = notice;
  435.             req->writeQFile();
  436.         }
  437.         traceQueue("SEND BLOCKED BY CONCURRENT CALL: \"%s\" in %s",
  438.             (char*) req->qfile, fmtTime(job->tts - now));
  439.         } else {
  440.         job->tts = requeueTTS(now);
  441.         req->notice = "Blocked by concurrent job";
  442.         req->writeQFile();
  443.         traceQueue("SEND BLOCKED BY CONCURRENT JOB: \"%s\" by \"%s\"",
  444.             (char*) req->qfile, (char*) info.getJobInProgress(),
  445.             fmtTime(job->tts - now));
  446.         }
  447.         insertJob(new Job(job));
  448.         delete req;
  449.         return;
  450.     }
  451.     if (info.getRejectNotice() == "") {
  452.         sendJob(req, info);
  453.         return;
  454.     }
  455.     /*
  456.      * Calls to this remote machine are being rejected
  457.      * for a specified reason that we return to the sender.
  458.      * Note that we clear the job in progress field since
  459.      * the rejection notice could have been placed in the
  460.      * file since our first call.
  461.      */
  462.     info.setJobInProgress("");
  463.     req->status = send_failed;
  464.     req->notice = info.getRejectNotice();
  465.     } else {
  466.     req->status = send_failed;
  467.     req->notice = "Unable to convert phone number to canonical format";
  468.     }
  469.     traceServer("SEND: REJECT: %s because: %s",
  470.     (char*) req->qfile, (char*) req->notice);
  471.     deleteRequest(Job::done, req, TRUE);
  472. }
  473.  
  474. /*
  475.  * Carry out the work for a ``send job''.
  476.  * 
  477.  * Use the remote machine's capabilities to convert
  478.  * and preformat documents.  In particular, we use this
  479.  * info to decide on the page size, whether or not high
  480.  * res is permissible, and whether or not 2d encoding
  481.  * should be used in preparing the facsimile to be sent.
  482.  */
  483. void
  484. faxServerApp::sendJob(FaxRequest* req, FaxMachineInfo& info)
  485. {
  486.     traceQueue("JOB \"%s\"", (char*) req->qfile);
  487.     const char* ostatus = FaxServer::stateStatus[server->state];
  488.     server->setServerStatus("Preparing job %s for transmission",
  489.     (char*) req->jobid);
  490.     JobStatus status = Job::done;
  491.     fxBool updateQFile = FALSE;
  492.     fxStr temp;        // NB: here to avoid compiler complaint
  493.     for (u_int i = 0; i < req->ops.length(); i++) {
  494.     const fxStr& file = req->files[i];
  495.     switch (req->ops[i]) {
  496.     case send_postscript:        // convert PostScript
  497.         temp = file | "+";
  498.         status = convertPostScript(file, temp, *req, info);
  499.         if (status == Job::done) {
  500.         /*
  501.          * Insert converted file into list and mark the
  502.          * PostScript document so that it's saved, but
  503.          * not processed again.  The converted file
  504.          * is sent, while the saved file is kept around
  505.          * in case it needs to be returned to the sender.
  506.          */
  507.         req->files.insert(temp, i+1);
  508.         req->ops.insert(FaxSendOp(send_tiff), i+1);
  509.         req->ops[i] = send_postscript_saved;
  510.         updateQFile = TRUE;
  511.         } else
  512.         (void) unlink((char*) temp);// bail out
  513.         break;
  514.     case send_tiff:            // verify format
  515.         status = checkFileFormat(file, info, req->notice);
  516.         break;
  517.     }
  518.     if (status != Job::done)
  519.         break;
  520.     }
  521.     if (status == Job::done) {
  522.     /*
  523.      * Write the q file before we start the send
  524.      * to make sure there is consistent state on
  525.      * disk should something happen during the send.
  526.      */
  527.     if (updateQFile)
  528.         req->writeQFile();
  529.     /*
  530.      * Everything should now be converted and compatible
  531.      * with the local and remote fax modems; start the job.
  532.      */
  533.     traceServer("SEND BEGIN: %s TO %s FROM %s",
  534.         (char*) req->qfile,
  535.         (char*) req->external,
  536.         (char*) req->sender);
  537.     jobStart = fileStart = time(0);
  538.     curreq = req;
  539.     server->sendFax(*req, info);
  540.     } else {
  541.     deleteRequest(status, req, TRUE);
  542.     server->setServerStatus(ostatus);
  543.     }
  544. }
  545.  
  546. /*
  547.  * Invoke the PostScript interpreter to image
  548.  * the document according to the capabilities
  549.  * of the remote fax machine.
  550.  */
  551. JobStatus
  552. faxServerApp::convertPostScript(const fxStr& inFile, const fxStr& outFile,
  553.     FaxRequest& req, const FaxMachineInfo& info)
  554. {
  555.     float resolution = req.resolution;
  556.     if (resolution > 150 &&
  557.       (!info.getSupportsHighRes() || !server->modemSupportsVRes(resolution)))
  558.     resolution = 98;    // guaranteed to be supported
  559.     /*
  560.      * Convert pagewidth in mm to pixel width to one
  561.      * of the possible G3 values.  Note that we don't
  562.      * handle the reduce image widths (e.g. middle
  563.      * 1216 pixels out of 1728 on a 151mm line).
  564.      */
  565.     u_int pageWidth = (req.pagewidth == 255 ? 2048 :
  566.                req.pagewidth == 303 ? 2432 :
  567.                           1728);
  568.     pageWidth = fxmin(pageWidth, (u_int) info.getMaxPageWidth());
  569.     float pageLength = req.pagelength;
  570.     if (info.getMaxPageLength() != -1 && pageLength > info.getMaxPageLength())
  571.     pageLength = info.getMaxPageLength();
  572.     /*
  573.      * ps2fax:
  574.      *   -o file        output (temp) file
  575.      *   -r <res>        output resolution (dpi)
  576.      *   -w <pagewidth>        output page width (pixels)
  577.      *   -l <pagelength>    output page length (mm)
  578.      *   [-1|-2]        1d or 2d encoding
  579.      */
  580.     char buf[1024];
  581.     /*
  582.      * Generate 2D-encoded facsimile if:
  583.      * o the server is permitted to generate 2D-encoded data,
  584.      * o the remote side is known to be capable of it, and
  585.      * o the modem is capable of sending 2D-encoded data.
  586.      */
  587.     int encoding = 1 + (
  588.     use2D &&
  589.     info.getCalledBefore() && info.getSupports2DEncoding() &&
  590.     server->modemSupports2D()
  591.     );
  592.     sprintf(buf, " -r %g -w %d -l %g -%d ",
  593.     resolution, pageWidth, pageLength, encoding);
  594.     fxStr cmd(ps2faxCmd | " -o " | outFile | buf | inFile | " 2>&1");
  595.     traceQueue("CONVERT POSTSCRIPT: \"%s\"", (char*) cmd);
  596.     JobStatus status;
  597.     fxStr output;
  598.     FILE* fp = popen(cmd, "r");
  599.     if (fp) {
  600.     /*
  601.      * Collect the output from the interpreter
  602.      * in case there is an error -- this is sent
  603.      * back to the user that submitted the job.
  604.      */
  605.     int n;
  606.     while ((n = fread(buf, 1, sizeof (buf), fp)) > 0) {
  607.         for (int i = 0; i < n; i++)
  608.         if (!isprint(buf[i]) && !isspace(buf[i]))
  609.             buf[i] = '?';        // convert garbage to "?"'s
  610.         output.append(buf, n);
  611.     }
  612.     int exitstat = pclose(fp);
  613.     if (exitstat != 0) {
  614.         status = Job::format_failed;
  615.         req.notice = output;        // return to sender
  616.         logError("CONVERT POSTSCRIPT: \"%s\" failed (exit %#d)",
  617.         (char*) cmd, exitstat);
  618.     } else
  619.         status = Job::done;
  620.     } else {
  621.     logError("CONVERT POSTSCRIPT: \"%s\" failed (popen)", (char*) cmd);
  622.     status = Job::no_formatter;
  623.     }
  624.     return (status);
  625. }
  626.  
  627. /*
  628.  * Verify the format of a TIFF file against the capabilities
  629.  * of the server's modem and the remote facsimile machine.
  630.  */
  631. JobStatus
  632. faxServerApp::checkFileFormat(const fxStr& file, const FaxMachineInfo& info,
  633.     fxStr& emsg)
  634. {
  635.     JobStatus status;
  636.     TIFF* tif = TIFFOpen(file, "r");
  637.     if (tif) {
  638.     status = Job::done;
  639.     do {
  640.         if (!checkPageFormat(tif, info, emsg))
  641.         status = Job::file_rejected;
  642.     } while (status == Job::done && TIFFReadDirectory(tif));
  643.     TIFFClose(tif);
  644.     } else {
  645.     struct stat sb;
  646.     if (stat((char*) file, &sb) < 0)
  647.         emsg = "Can not open document file " | file;
  648.     else
  649.         emsg = "Document file is not valid TIFF "
  650.         "(check for PostScript conversion problems)";
  651.     status = Job::file_rejected;
  652.     }
  653.     if (status != Job::done)
  654.     logError("SEND: REJECT %s because: \"%s\"",
  655.         (char*) file, (char*) emsg);
  656.     return (status);
  657. }
  658.  
  659. /*
  660.  * Check the format of a page against the capabilities
  661.  * of the modem (or the default capabilities if no modem
  662.  * is currently setup).
  663.  */
  664. fxBool
  665. faxServerApp::checkPageFormat(TIFF* tif, const FaxMachineInfo& info, fxStr& emsg)
  666. {
  667.     short bitspersample;
  668.     TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);
  669.     if (bitspersample != 1) {
  670.     emsg = fxStr(bitspersample, "Not a bilevel image (bits/sample %u)");
  671.     return (FALSE);
  672.     }
  673.     short samplesperpixel;
  674.     TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
  675.     if (samplesperpixel != 1) {
  676.     emsg = fxStr(samplesperpixel, "Multi-sample data (samples %u)");
  677.     return (FALSE);
  678.     }
  679.     short compression = 0;
  680.     (void) TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
  681.     if (compression != COMPRESSION_CCITTFAX3) {
  682.     emsg = "Not in Group 3 format";
  683.     return (FALSE);
  684.     }
  685.     long g3opts = 0;
  686.     (void) TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &g3opts);
  687.     if (g3opts & GROUP3OPT_2DENCODING) {
  688.     if (info.getCalledBefore() && !info.getSupports2DEncoding()) {
  689.         emsg = "Client is incapable of receiving 2DMR-encoded documents";
  690.         return (FALSE);
  691.     }
  692.     if (!server->modemSupports2D()) {
  693.         emsg = "Modem is incapable of sending 2DMR-encoded documents";
  694.         return (FALSE);
  695.     }
  696.     }
  697.     u_long w;
  698.     if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w)) {
  699.     emsg = "Malformed document (no image width)";
  700.     return (FALSE);
  701.     }
  702.     if (w > info.getMaxPageWidth()) {
  703.     emsg = fxStr((long) w,
  704.         "Client is incapable of receiving document with page width %lu");
  705.     return (FALSE);
  706.     }
  707.     if (!server->modemSupportsPageWidth((u_int) w)) {
  708.     emsg = fxStr((long) w,
  709.          "Modem is incapable of sending document with page width %lu");
  710.     return (FALSE);
  711.     }
  712.  
  713.     /*
  714.      * Try to deduce the vertical resolution of the image
  715.      * image.  This can be problematical for arbitrary TIFF
  716.      * images because vendors sometimes do not give the units.
  717.      * We, however, can depend on the info in images that
  718.      * we generate because we are careful to include valid info.
  719.      */
  720.     float yres;
  721.     if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres)) {
  722.     short resunit = RESUNIT_NONE;
  723.     (void) TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
  724.     if (resunit == RESUNIT_CENTIMETER)
  725.         yres *= 25.4;
  726.     } else {
  727.     /*
  728.      * No vertical resolution is specified, try
  729.      * to deduce one from the image length.
  730.      */
  731.     u_long l;
  732.     TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &l);
  733.     yres = (l < 1450 ? 98 : 196);        // B4 at 98 lpi is ~1400 lines
  734.     }
  735.     if (yres >= 150 && !info.getSupportsHighRes()) {
  736.     emsg = "Client is incapable of receiving high resolution documents";
  737.     return (FALSE);
  738.     }
  739.     if (!server->modemSupportsVRes(yres)) {
  740.     emsg = "Modem is incapable of sending high resolution documents";
  741.     return (FALSE);
  742.     }
  743.  
  744.     /*
  745.      * Select page length according to the image size and
  746.      * vertical resolution.  Note that if the resolution
  747.      * info is bogus, we may select the wrong page size.
  748.      * Note also that we're a bit lenient in places here
  749.      * to take into account sloppy coding practice (e.g.
  750.      * using 200 lpi for high-res facsimile.)
  751.      */
  752.     u_long h = 0;
  753.     if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h)) {
  754.     emsg = "Malformed document (no image length)";
  755.     return (FALSE);
  756.     }
  757.     float len = h / (yres == 0 ? 1. : yres);        // page length in mm
  758.     if (info.getMaxPageLength() != -1 && len > info.getMaxPageLength()+30) {
  759.     emsg = fxStr((long) len,
  760.         "Client is incapable of receiving document with page length %lu");
  761.     return (FALSE);
  762.     }
  763.     if (!server->modemSupportsPageLength((u_int) len)) {
  764.     emsg = fxStr((long) len,
  765.         "Modem is incapable of sending document with page length %lu");
  766.     return (FALSE);
  767.     }
  768.     return (TRUE);
  769. }
  770.  
  771. /*
  772.  * Insert a job in the queue according to
  773.  * the time-to-send criteria.  The queue
  774.  * is maintained sorted by the tts values.
  775.  */
  776. void
  777. faxServerApp::insertJob(Job* job)
  778. {
  779.     Job* jp;
  780.     Job** jpp = &queue;
  781.     for (; (jp = *jpp) && jp->tts < job->tts; jpp = &jp->next)
  782.     ;
  783.     job->next = jp;
  784.     *jpp = job;
  785. }
  786.  
  787. /*
  788.  * Remove a job from the queue and update the time-to-send
  789.  * values of any remaining jobs.  Also, if necessary, adjust
  790.  * the current timeout.
  791.  */
  792. Job*
  793. faxServerApp::removeJob(const fxStr& name)
  794. {
  795.     Job* jp;
  796.     for (Job** jpp = &queue; (jp = *jpp) && jp->file != name; jpp = &jp->next)
  797.     ;
  798.     if (jp) {
  799.     if (*jpp = jp->next) {
  800.         if (jpp == &queue && currentTimeout != 0) {    // adjust timeout
  801.         stopTimer();
  802.         startTimer(queue->tts);
  803.         }
  804.     }
  805.     }
  806.     return (jp);
  807. }
  808.  
  809. /*
  810.  * Start the timer associated with scanning the job queue.
  811.  * Note that we maintain the expected time the timer will
  812.  * expire so that we can calculate how long it's been running
  813.  * in case we prematurely stop the timer.
  814.  */
  815. void
  816. faxServerApp::startTimer(u_long sec)
  817. {
  818.     time_t now = time(0);
  819.     traceQueue("JOB TIMER START %s", fmtTime(sec - now));
  820.     if (sec > 0) {        // should always be later
  821.     currentTimeout = sec - now;
  822.     Dispatcher::instance().startTimer(currentTimeout, 0, this);
  823.     } else
  824.     logError("Zero-time timer requested, server may be hosed");
  825. }
  826.  
  827. /*
  828.  * Stop any current timer and adjust the
  829.  * tts of the job at the head of the queue.
  830.  */
  831. void
  832. faxServerApp::stopTimer()
  833. {
  834.     if (currentTimeout != 0) {
  835.     Dispatcher::instance().stopTimer(this);
  836.     currentTimeout = 0;
  837.     if (queue)
  838.         traceQueue("JOB TIMER STOP (Q head %s tts %s)",
  839.         (char*) queue->file, fmtTime(queue->tts - time(0)));
  840.     else
  841.         traceQueue("JOB TIMER STOP (Q empty)");
  842.     }
  843. }
  844.  
  845. /*
  846.  * Delete a queued job.
  847.  */
  848. void
  849. faxServerApp::deleteJob(const fxStr& name)
  850. {
  851.     Job* jp = removeJob(name);
  852.     if (jp)
  853.     delete jp;
  854. }
  855.  
  856. /*
  857.  * Alter job parameters.
  858.  */
  859. fxBool
  860. faxServerApp::alterJob(const char* s)
  861. {
  862.     const char cmd = *s++;
  863.     const char* cp = strchr(s, ' ');
  864.     if (!cp) {
  865.     logError("Malformed JOB request \"%s\"", s);
  866.     return (FALSE);
  867.     }
  868.     fxStr name(s, cp-s);
  869.     Job* jp = removeJob(name);
  870.     if (!jp) {
  871.     logError("JOB \"%s\" not found on the queue.", (char*) name);
  872.     return (FALSE);
  873.     }
  874.     while (isspace(*cp))
  875.     cp++;
  876.     switch (cmd) {
  877.     case 'T':            // time-to-send
  878.     jp->tts = atoi(cp);
  879.     break;
  880.     case 'P':            // change priority
  881.     jp->pri = atoi(cp);
  882.     break;
  883.     default:
  884.     logError("Invalid JOB request command \"%c\" ignored.", cmd);
  885.     break;
  886.     }
  887.     insertJob(jp);        // place back on queue
  888.     return (TRUE);
  889. }
  890.  
  891. /*
  892.  * Scan the list of jobs and process those that are
  893.  * ready to send (time-to-send is <= 0).  This method
  894.  * is invoked from the following places:
  895.  *
  896.  * o in response to a FIFO command that creates a new job or
  897.  *   that alters the parameters of an existing job
  898.  * o in response to a scheduler timeout
  899.  * o by the fax server thread when a modem changes state from
  900.  *   busy/unavailable to ready for use
  901.  *
  902.  * Note that since we are a pseduo-multi-threaded application
  903.  * we guard against recursive invocations by checking if there
  904.  * is a current request being processed.  This handles the case
  905.  * where we are processing a job and, for example, receive a
  906.  * FIFO message that causes us to be invoked.
  907.  */
  908. void
  909. faxServerApp::scanJobQueue()
  910. {
  911.     if (queue && curreq == NULL) {
  912.     stopTimer();
  913.     while (queue != NULL) {
  914.         /*
  915.          * If the job at the head of the queue isn't
  916.          * ready to send yet, then restart the timer.
  917.          * Note that we don't need to scan the entire
  918.          * queue because we organize it s.t. the entry
  919.          * at the front is always the first to be ready.
  920.          */
  921.         if (queue->tts > time(0)) {
  922.         startTimer(queue->tts);
  923.         break;
  924.         }
  925.         /*
  926.          * Don't try to process anything if the server
  927.          * thread is busy doing something else (like
  928.          * waiting for a getty to exit).  We'll be notified
  929.          * through notifyModemReady when the server+modem
  930.          * are once again available.
  931.          */
  932.         if (server->serverBusy())
  933.         break;
  934.         /*
  935.          * The job at the head of the queue should be processed.
  936.          * Remove it from the queue and initiate the work.  If
  937.          * the job must be requeued, a new ``job'' will be created
  938.          * (could be optimized).
  939.          */
  940.         Job* job = queue;
  941.         queue = job->next;
  942.         processJob(job);
  943.         delete job;
  944.     }
  945.     }
  946. }
  947.  
  948. struct FaxAcctInfo {
  949.     const char*    user;        // sender/receiver identity
  950.     time_t    start;        // starting time
  951.     time_t    duration;    // job duration (minutes)
  952.     const char*    device;        // modem device
  953.     const char*    dest;        // receiver phone number
  954.     const char*    csi;        // remote csi
  955.     u_int    npages;        // pages successfully sent/rcvd
  956.     u_int    sigrate;    // negotiated signalling rate
  957.     const char*    df;        // negotiated data format
  958.     const char*    status;        // status info (optional)
  959. };
  960.  
  961. /*
  962.  * Handle notification of a send job completing.
  963.  */
  964. void
  965. faxServerApp::notifySendDone(FaxRequest* req,
  966.     u_int npages, const char* csi, u_int sigrate, const char* df)
  967. {
  968.     if (req != NULL) {            // XXX should never be NULL
  969.     FaxAcctInfo ai;
  970.     ai.user = req->mailaddr;
  971.     ai.start = jobStart;
  972.     ai.duration = time(0) - jobStart;
  973.     ai.device = devID;
  974.     ai.dest = req->external;
  975.     ai.csi = csi;
  976.     ai.npages = npages;
  977.     ai.sigrate = sigrate;
  978.     ai.df = df;
  979.     ai.status = req->notice;
  980.     if (req->status == send_done)
  981.         ai.status = "";
  982.     account("SEND", ai);
  983.     }
  984.     requestComplete(req, TRUE);
  985. }
  986.  
  987. /*
  988.  * Handle the completion of processing of a fax request.
  989.  * We free up any resources associated with the request
  990.  * and then process the next entry in the queue or restart
  991.  * the scan timer.
  992.  */
  993. void
  994. faxServerApp::requestComplete(FaxRequest* req, fxBool notify)
  995. {
  996.     if (req != NULL) {            // XXX should never be NULL
  997.     if (req == curreq)
  998.         curreq = NULL;
  999.     time_t now = time(0);
  1000.     if (req->status == send_retry) {// send should be retried
  1001.         if (req->tts <= now) {
  1002.         /*
  1003.          * Send failed, bump it's ``time to send''
  1004.          * and rewrite the queue file.  This causes
  1005.          * the job to be rescheduled for transmission
  1006.          * at a future time.
  1007.          */
  1008.         req->tts = requeueTTS(now);
  1009.         }
  1010.         traceServer("REQUEUE %s FOR %s, REASON \"%s\"",
  1011.         (char*) req->qfile,
  1012.         fmtTime(req->tts - now),
  1013.         (char*) req->notice);
  1014.         req->writeQFile();
  1015.         if (notify && req->notify == FaxRequest::when_requeued)
  1016.         notifySender(Job::requeued, *req); 
  1017.         insertJob(new Job(req->qfile, req->tts));
  1018.         delete req;                // implicit unlock of q file
  1019.     } else {
  1020.         traceServer("SEND DONE: %s", fmtTime(now - jobStart));
  1021.         // NB: always notify client if job failed
  1022.         deleteRequest(Job::done, req, req->status == send_failed);
  1023.     }
  1024.     } else
  1025.     logError("notifyJobComplete called with a NULL request");
  1026. }
  1027.  
  1028. /*
  1029.  * Notify the sender of the facsimile that something has
  1030.  * happened -- the job has completed, it's been requeued
  1031.  * for later transmission, etc.
  1032.  */
  1033. void
  1034. faxServerApp::notifySender(JobStatus why, FaxRequest& req)
  1035. {
  1036.     traceServer("NOTIFY %s\n", (char*) req.mailaddr);
  1037.     static const char* whys[] = {
  1038.     "no_status",
  1039.     "done",
  1040.     "requeued",
  1041.     "removed",
  1042.     "timedout",
  1043.     "no_formatter",
  1044.     "format_failed",
  1045.     "poll_rejected",
  1046.     "poll_no_document",
  1047.     "poll_failed",
  1048.     "file_rejected",
  1049.     "killed",
  1050.     };
  1051.     fxStr whystr(whys[why]);
  1052.     if (why == Job::done && req.status != send_done)
  1053.     whystr = "failed";
  1054.     fxStr quote(" \""); fxStr enquote("\"");
  1055.     // NB: use a temporary here to avoid gcc bug
  1056.     fxStr canon(server->canonicalizePhoneNumber(req.external));
  1057.     fxStr cmd(notifyCmd
  1058.     |   " " | req.qfile
  1059.     | quote | whystr | enquote
  1060.     | quote | fmtTime(time(0) - jobStart) | enquote
  1061.     |   " " | serverPID
  1062.     | quote | canon | enquote
  1063.     );
  1064.     if (why == Job::requeued) {
  1065.     /*
  1066.      * It's too hard to do localtime in an awk script,
  1067.      * so if we may need it, we calculate it here
  1068.      * and pass the result as an optional argument.
  1069.      */
  1070.     char buf[30];
  1071.     strftime(buf, sizeof (buf), " \"%H:%M\"", localtime(&req.tts));
  1072.     cmd.append(buf);
  1073.     }
  1074.     runCmd(cmd, TRUE);
  1075. }
  1076.  
  1077. /*
  1078.  * Open the specified FIFO file.
  1079.  */
  1080. int
  1081. faxServerApp::openFIFO(const char* fifoName, int mode, fxBool okToExist)
  1082. {
  1083.     if (mkfifo(fifoName, mode & 0777) < 0) {
  1084.     if (errno != EEXIST || !okToExist)
  1085.         fxFatal("Could not create FIFO \"%s\".", fifoName);
  1086.     }
  1087.     int fd = ::open(fifoName, CONFIG_OPENFIFO|O_NDELAY, 0);
  1088.     if (fd == -1)
  1089.     fxFatal("Could not open FIFO file \"%s\"", fifoName);
  1090.     // open should set O_NDELAY, but just to be sure...
  1091.     if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NDELAY) < 0)
  1092.     logError("openFIFO: fcntl: %m");
  1093.     return (fd);
  1094. }
  1095.  
  1096. /*
  1097.  * Respond to input on a FIFO.
  1098.  */
  1099. int
  1100. faxServerApp::inputReady(int fd)
  1101. {
  1102.     char buf[2048];
  1103.     int n;
  1104.     while ((n = ::read(fd, buf, sizeof (buf)-1)) > 0) {
  1105.     buf[n] = '\0';
  1106.     /*
  1107.      * Break up '\0'-separated records and strip
  1108.      * any trailing '\n' so that "echo mumble>FIFO"
  1109.      * works (i.e. echo appends a '\n' character).
  1110.      */
  1111.     char* bp = &buf[0];
  1112.     do {
  1113.         char* cp = strchr(bp, '\0');
  1114.         if (cp > bp) {
  1115.         if (cp[-1] == '\n')
  1116.             cp[-1] = '\0';
  1117.         fifoMessage(bp);
  1118.         }
  1119.         bp = cp+1;
  1120.     } while (bp < &buf[n]);
  1121.     }
  1122. #ifdef FIFOSELECTBUG
  1123.     /*
  1124.      * Solaris 2.x botch (and some versions of IRIX 5.x).
  1125.      *
  1126.      * A client close of an open FIFO causes an M_HANGUP to be
  1127.      * sent and results in the receiver's file descriptor being
  1128.      * marked ``hung up''.  This in turn causes select to
  1129.      * perpetually return true and if we're running as a realtime
  1130.      * process, brings the system to a halt.  The workaround for
  1131.      * Solaris 2.1 was to do a parallel reopen of the appropriate
  1132.      * FIFO so that the original descriptor is recycled.  This
  1133.      * apparently no longer works in Solaris 2.2 or later and we
  1134.      * are forced to close and reopen both FIFO descriptors (noone
  1135.      * appears capable of answering why this this is necessary and
  1136.      * I personally don't care...)
  1137.      */
  1138.     ::close(fifo); fifo = openFIFO(fifoName, 0600, TRUE);
  1139.     Dispatcher::instance().link(fifo, Dispatcher::ReadMask, this);
  1140.     ::close(devfifo); devfifo = openFIFO(fifoName | "." | devID, 0600, TRUE);
  1141.     Dispatcher::instance().link(devfifo, Dispatcher::ReadMask, this);
  1142. #endif
  1143.     return (0);
  1144. }
  1145.  
  1146. /*
  1147.  * Process a message received through a FIFO.
  1148.  */
  1149. void
  1150. faxServerApp::fifoMessage(const char* cp)
  1151. {
  1152.     switch (cp[0]) {
  1153.     case 'A':                // answer the phone
  1154.     if (cp[1] != '\0') {
  1155.         traceServer("ANSWER %s", cp+1);
  1156.         if (streq(cp+1, "fax"))
  1157.         server->answerPhone(FaxModem::ANSTYPE_FAX, TRUE);
  1158.         else if (streq(cp+1, "data"))
  1159.         server->answerPhone(FaxModem::ANSTYPE_DATA, TRUE);
  1160.         else if (streq(cp+1, "voice"))
  1161.         server->answerPhone(FaxModem::ANSTYPE_VOICE, TRUE);
  1162.     } else {
  1163.         traceServer("ANSWER");
  1164.         server->answerPhone(FaxModem::ANSTYPE_ANY, TRUE);
  1165.     }
  1166.     break;
  1167.     case 'M':                // modem control
  1168.     traceServer("MODEM \"%s\"", cp+1);
  1169.     server->restoreStateItem(cp+1);
  1170.     break;
  1171.     case 'J':                // alter job parameter(s)
  1172.     traceServer("JOB PARAMS \"%s\"", cp+1);
  1173.     if (curreq == NULL || curreq->qfile != cp+1) {
  1174.         if (alterJob(cp+1))
  1175.         scanJobQueue();
  1176.     } else
  1177.         ; // XXX can't alter parameters of job being processed
  1178.     break;
  1179.     case 'Q':                // quit
  1180.     traceServer("QUIT");
  1181.     faxServerApp::close();
  1182.     break;
  1183.     case 'R':                // remove job
  1184.     case 'K':                // kill job
  1185.     traceServer("%s \"%s\"", (cp[0] == 'R' ? "REMOVE" : "KILL"), cp+1);
  1186.     if (curreq == NULL || curreq->qfile != cp+1) {
  1187.         deleteJob(cp+1);
  1188.         // NOTE: actual removal normally done by faxd.recv
  1189.         int fd = ::open(cp+1, O_RDWR);
  1190.         if (fd > 0) {
  1191.         if (flock(fd, LOCK_EX|LOCK_NB) >= 0) {
  1192.             FaxRequest* req = new FaxRequest(cp+1);
  1193.             if (req) {
  1194.             (void) req->readQFile(fd);
  1195.             if (cp[0] == 'K')
  1196.                 deleteRequest(Job::killed, req, TRUE);
  1197.             else
  1198.                 deleteRequest(Job::removed, req, FALSE);
  1199.             }
  1200.         }
  1201.         ::close(fd);
  1202.         }
  1203.         scanJobQueue();
  1204.     } else
  1205.         // aborting the job causes it to be purged
  1206.         server->abortSession();
  1207.     break;
  1208.     case 'S':                // submit a send job
  1209.     traceServer("SUBMIT \"%s\"", cp+1);
  1210.     /*
  1211.      * Insert the new job in the queue and if something
  1212.      * is not already being processed, scan the queue.
  1213.      */
  1214.     insertJob(new Job(cp+1, time(0)));
  1215.     scanJobQueue();
  1216.     break;
  1217.     default:
  1218.     logError("bad fifo message \"%s\"", cp);
  1219.     break;
  1220.     }
  1221. }
  1222.  
  1223. /*
  1224.  * Delete a request and associated state.
  1225.  */
  1226. void
  1227. faxServerApp::deleteRequest(JobStatus why, FaxRequest* req, fxBool force)
  1228. {
  1229.     if (req->notify != FaxRequest::no_notice || force) {
  1230.     req->writeQFile();        // update disk copy for notification use
  1231.     notifySender(why, *req);
  1232.     }
  1233.     for (u_int i = 0, n = req->files.length(); i < n; i++) {
  1234.     if (req->ops[i] != send_poll)
  1235.         (void) unlink((char*) req->files[i]);
  1236.     }
  1237.     (void) unlink((char*) req->qfile);
  1238.     delete req;
  1239. }
  1240.  
  1241. /*
  1242.  * Handle notification that the modem device has become
  1243.  * available again after a period of being unavailable.
  1244.  */
  1245. void
  1246. faxServerApp::notifyModemReady()
  1247. {
  1248.     scanJobQueue();
  1249. }
  1250.  
  1251. #define    isSavedOp(op) \
  1252.     ((op) == send_tiff_saved || (op) == send_postscript_saved)
  1253.  
  1254. /*
  1255.  * Handle notification that a document has been successfully
  1256.  * transmitted.  We remove the file from the request array so
  1257.  * that it's not resent if the job is requeued.
  1258.  */
  1259. void
  1260. faxServerApp::notifyDocumentSent(FaxRequest& req, u_int fi)
  1261. {
  1262.     time_t now = time(0);
  1263.     traceServer("SEND: FROM %s TO %s (%s sent in %s)",
  1264.     (char*) req.mailaddr, (char*) req.external, (char*) req.files[fi],
  1265.     fmtTime(now - fileStart));
  1266.     fileStart = now;            // for next file
  1267.     if (req.ops[fi] == send_tiff) {
  1268.     (void) unlink((char*) req.files[fi]);
  1269.     u_int n = 1;
  1270.     if (fi > 0 && isSavedOp(req.ops[fi-1])) {
  1271.         /*
  1272.          * Document sent was converted from another; delete
  1273.          * the original as well.  (Or perhaps we should hold
  1274.          * onto it to return to sender in case of a problem?)
  1275.          */
  1276.         (void) unlink((char*) req.files[fi-1]);
  1277.         fi--, n++;
  1278.     }
  1279.     req.files.remove(fi, n);
  1280.     req.ops.remove(fi, n);
  1281.     req.writeQFile();
  1282.     } else
  1283.     logError("notifyDocumentSent called for non-TIFF file");
  1284. }
  1285.  
  1286. /*
  1287.  * Handle notification of a document received as a
  1288.  * result of a poll request.
  1289.  */
  1290. void
  1291. faxServerApp::notifyPollRecvd(FaxRequest& req, const FaxRecvInfo& ri)
  1292. {
  1293.     recordRecv(ri);
  1294.     // hand to delivery/notification command
  1295.     runCmd(pollRcvdCmd
  1296.      | " " | req.mailaddr
  1297.      | " " | ri.qfile
  1298.      | " " | fxStr(ri.time / 60.,"%.2f")
  1299.      | " " | fxStr((int) ri.sigrate, "%u")
  1300.      | " \"" | ri.protocol | "\""
  1301.      | " \"" | ri.reason | "\""
  1302.      , TRUE);
  1303. }
  1304.  
  1305. /*
  1306.  * Handle notification that a poll operation has been
  1307.  * successfully completed.  Note that any received
  1308.  * documents have already been passed to notifyPollRecvd.
  1309.  */
  1310. void
  1311. faxServerApp::notifyPollDone(FaxRequest& req, u_int pi)
  1312. {
  1313.     time_t now = time(0);
  1314.     traceServer("POLL: BY %s TO %s completed in %s",
  1315.     (char*) req.mailaddr, (char*) req.external, fmtTime(now - fileStart));
  1316.     fileStart = now;
  1317.     if (req.ops[pi] == send_poll) {
  1318.     req.files.remove(pi);
  1319.     req.ops.remove(pi);
  1320.     req.writeQFile();
  1321.     } else
  1322.     logError("notifyPollDone called for non-poll request");
  1323. }
  1324.  
  1325. /*
  1326.  * Handle notification that a document has been received.
  1327.  */
  1328. void
  1329. faxServerApp::notifyRecvDone(const FaxRecvInfo& ri)
  1330. {
  1331.     recordRecv(ri);
  1332.     // hand to delivery/notification command
  1333.     runCmd(faxRcvdCmd
  1334.      | " " | ri.qfile
  1335.      | " " | fxStr(ri.time / 60.,"%.2f")
  1336.      | " " | fxStr((int) ri.sigrate, "%u")
  1337.      | " \"" | ri.protocol | "\""
  1338.      | " \"" | ri.reason | "\""
  1339.      , TRUE);
  1340. }
  1341.  
  1342. void
  1343. faxServerApp::recordRecv(const FaxRecvInfo& ri)
  1344. {
  1345.     char type[80];
  1346.     if (ri.pagelength == 297 || ri.pagelength == -1)
  1347.     strcpy(type, "A4");
  1348.     else if (ri.pagelength == 364)
  1349.     strcpy(type, "B4");
  1350.     else
  1351.     sprintf(type, "(%u x %.2f)", ri.pagewidth, ri.pagelength);
  1352.     traceServer("RECV: %s from %s, %d %s pages, %g dpi, %s, %s at %u baud",
  1353.     (char*) ri.qfile, (char*) ri.sender,
  1354.     ri.npages, type, ri.resolution,
  1355.     (char*) ri.protocol, fmtTime((time_t) ri.time), ri.sigrate);
  1356.  
  1357.     FaxAcctInfo ai;
  1358.     ai.user = "fax";
  1359.     ai.duration = (time_t) ri.time;
  1360.     ai.start = time(0) - ai.duration;
  1361.     ai.device = devID;
  1362.     ai.dest = server->getModemNumber();
  1363.     ai.csi = ri.sender;
  1364.     ai.npages = ri.npages;
  1365.     ai.sigrate = ri.sigrate;
  1366.     ai.df = ri.protocol;
  1367.     ai.status = ri.reason;
  1368.     account("RECV", ai);
  1369. }
  1370.  
  1371. /*
  1372.  * Process a timer expiring.
  1373.  */
  1374. void
  1375. faxServerApp::timerExpired(long, long)
  1376. {
  1377.     if (queue) {
  1378.     currentTimeout = 0;
  1379.     scanJobQueue();
  1380.     }
  1381. }
  1382.  
  1383. /*
  1384.  * Create a FaxRequest and read the
  1385.  * associated queue file into it.
  1386.  */
  1387. FaxRequest*
  1388. faxServerApp::readQFile(const fxStr& filename, int fd)
  1389. {
  1390.     FaxRequest* req = new FaxRequest(filename);
  1391.     if (req->readQFile(fd)) {
  1392.     if (req->external == "")
  1393.         req->external = server->canonicalizePhoneNumber(req->number);
  1394.     } else
  1395.     delete req, req = NULL;
  1396.     return req;
  1397. }
  1398.  
  1399. /*
  1400.  * Force the real uid+gid to be the same as
  1401.  * the effective ones.  Must temporarily
  1402.  * make the effective uid root in order to
  1403.  * do the real id manipulations.
  1404.  */
  1405. void
  1406. faxServerApp::setRealIDs()
  1407. {
  1408.     uid_t euid = geteuid();
  1409.     if (seteuid(0) < 0)
  1410.     logError("seteuid(root): %m");
  1411.     if (setgid(getegid()) < 0)
  1412.     logError("setgid: %m");
  1413.     if (setuid(euid) < 0)
  1414.     logError("setuid: %m");
  1415. }
  1416.  
  1417. /*
  1418.  * Run the specified shell command.  If changeIDs is
  1419.  * true, we set the real uid+gid to the effective; this
  1420.  * is so that programs like sendmail show an informative
  1421.  * from address.
  1422.  */
  1423. void
  1424. faxServerApp::runCmd(const char* cmd, fxBool changeIDs)
  1425. {
  1426.     pid_t pid = fork();
  1427.     switch (pid) {
  1428.     case 0:
  1429.     if (changeIDs)
  1430.         setRealIDs();
  1431.     execl("/bin/sh", "sh", "-c", cmd, (char*) NULL);
  1432.     _exit(127);
  1433.     case -1:
  1434.     logError("Can not fork for \"%s\"", cmd);
  1435.     break;
  1436.     default:
  1437.     { int status = 0;
  1438.       (void) waitpid(pid, &status, 0);
  1439.       if (status != 0)
  1440.         logError("Bad exit status %#o for \"%s\"", status, cmd);
  1441.     }
  1442.     break;
  1443.     }
  1444. }
  1445.  
  1446. struct popenRecord {
  1447.     popenRecord* next;
  1448.     pid_t    pid;
  1449.     FILE*    fp;
  1450. };
  1451. popenRecord* faxServerApp::popenList = NULL;
  1452.  
  1453. /*
  1454.  * Internal version of popen/pclose.  These are
  1455.  * used instead of the normal libc routines so
  1456.  * that we can force the real uid in the child
  1457.  * process (for programs like at).
  1458.  */
  1459. FILE*
  1460. faxServerApp::popen(const char* cmd, const char* mode)
  1461. {
  1462.     popenRecord* pr = new popenRecord;
  1463.     if (!pr) {
  1464.     logError("popen: Out of memory");
  1465.     return (NULL);
  1466.     }
  1467.     int pfd[2];
  1468.     if (pipe(pfd) < 0) {
  1469.     logError("Can not create pipe for \"%s\"", cmd);
  1470.     delete pr;
  1471.     return (NULL);
  1472.     }
  1473.     pr->pid = fork();
  1474.     switch (pr->pid) {
  1475.     case -1:                // error
  1476.     logError("Can not fork for \"%s\"", cmd);
  1477.     ::close(pfd[0]);
  1478.     ::close(pfd[1]);
  1479.     delete pr;
  1480.     return (NULL);
  1481.     case 0:                // child
  1482.     /*
  1483.      * Force real uid to match effective.  This insures
  1484.      * that programs like at get the necessary environment.
  1485.      */
  1486.     setRealIDs();
  1487.     if (*mode == 'r') {
  1488.         if (pfd[1] != STDOUT_FILENO) {
  1489.         dup2(pfd[1], STDOUT_FILENO);
  1490.         ::close(pfd[1]);
  1491.         }
  1492.         ::close(pfd[0]);
  1493.     } else {
  1494.         if (pfd[0] != STDIN_FILENO) {
  1495.         dup2(pfd[0], STDIN_FILENO);
  1496.         ::close(pfd[0]);
  1497.         }
  1498.         ::close(pfd[1]);
  1499.     }
  1500.     execl("/bin/sh", "sh", "-c", cmd, (char*) NULL);
  1501.     _exit(127);
  1502.     /*NOTREACHED*/
  1503.     }
  1504.     // NB: we assume fdopen always succeeds
  1505.     if (*mode == 'r') {
  1506.     pr->fp = fdopen(pfd[0], mode);
  1507.     ::close(pfd[1]);
  1508.     } else {
  1509.     pr->fp = fdopen(pfd[1], mode);
  1510.     ::close(pfd[0]);
  1511.     }
  1512.     pr->next = popenList;
  1513.     popenList = pr;
  1514.     return pr->fp;
  1515. }
  1516.  
  1517. int
  1518. faxServerApp::pclose(FILE* fp)
  1519. {
  1520.     fclose(fp);
  1521.     popenRecord* pr;
  1522.     popenRecord** ppr;
  1523.     int status = -1;
  1524.     for (ppr = &popenList; pr = *ppr; ppr = &pr->next)
  1525.     if (pr->fp == fp) {
  1526.         *ppr = pr->next;                // remove from list
  1527.         (void) waitpid(pr->pid, &status, 0);    // reap process
  1528.         delete pr;                    // free record
  1529.         break;
  1530.     }
  1531.     return (status);
  1532. }
  1533.  
  1534. const char*
  1535. faxServerApp::fmtTime(time_t t)
  1536. {
  1537.     static char tbuf[10];
  1538.     const char* digits = "0123456789";
  1539.     char* cp = tbuf;
  1540.     long v;
  1541.  
  1542.     if ((v = t/3600) > 0) {
  1543.     if (v >= 10)
  1544.         *cp++ = digits[v / 10];
  1545.     *cp++ = digits[v % 10];
  1546.     *cp++ = ':';
  1547.     t -= v*3600;
  1548.     }
  1549.     v = t/60;
  1550.     if (v >= 10 || cp > tbuf)
  1551.     *cp++ = digits[v / 10];
  1552.     *cp++ = digits[v % 10];
  1553.     t -= v*60;
  1554.     *cp++ = ':';
  1555.     *cp++ = digits[t / 10];
  1556.     *cp++ = digits[t % 10];
  1557.     *cp = '\0';
  1558.     return tbuf;
  1559. }
  1560.  
  1561. void
  1562. faxServerApp::traceServer(const char* va_alist ...)
  1563. #define    fmt va_alist
  1564. {
  1565.     if (server->getServerTracing() & FAXTRACE_SERVER) {
  1566.     va_list ap;
  1567.     va_start(ap, va_alist);
  1568.     vlogInfo(fmt, ap);
  1569.     va_end(ap);
  1570.     }
  1571. }
  1572. #undef fmt
  1573.  
  1574. void
  1575. faxServerApp::traceQueue(const char* va_alist ...)
  1576. #define    fmt va_alist
  1577. {
  1578.     if (server->getServerTracing() & FAXTRACE_QUEUEMGMT) {
  1579.     va_list ap;
  1580.     va_start(ap, va_alist);
  1581.     vlogInfo(fmt, ap);
  1582.     va_end(ap);
  1583.     }
  1584. }
  1585. #undef fmt
  1586.  
  1587. /*
  1588.  * Record a transfer in the transfer log file.
  1589.  */
  1590. void
  1591. faxServerApp::account(const char* cmd, const FaxAcctInfo& ai)
  1592. {
  1593.     FILE* flog = fopen(FAX_XFERLOG, "a");
  1594.     if (flog != NULL) {
  1595.     flock(fileno(flog), LOCK_EX);
  1596.     char buf[80];
  1597.     strftime(buf, sizeof (buf), "%D %H:%M", localtime(&ai.start));
  1598.     fprintf(flog,
  1599.         "%s\t%s\t%s\t%s\t\"%s\"\t\"%s\"\t%d\t%s\t%d\t%s\t\"%s\"\n",
  1600.         buf, cmd, ai.device,
  1601.         ai.user,
  1602.         ai.dest, ai.csi,
  1603.         ai.sigrate, ai.df,
  1604.         ai.npages, fmtTime(ai.duration),
  1605.         ai.status);
  1606.     fclose(flog);
  1607.     } else
  1608.     logError("Can not open log file \"%s\"", FAX_XFERLOG);
  1609. }
  1610.  
  1611. void
  1612. faxServerApp::vlogInfo(const char* fmt, va_list ap)
  1613. {
  1614.     vsyslog(LOG_INFO, fmt, ap);
  1615. }
  1616.  
  1617. void
  1618. faxServerApp::logError(const char* va_alist ...)
  1619. #define    fmt va_alist
  1620. {
  1621.     va_list ap;
  1622.     va_start(ap, va_alist);
  1623.     vlogError(fmt, ap);
  1624.     va_end(ap);
  1625. }
  1626. #undef fmt
  1627.  
  1628. void
  1629. faxServerApp::vlogError(const char* fmt, va_list ap)
  1630. {
  1631.     vsyslog(LOG_ERR, fmt, ap);
  1632. }
  1633.  
  1634. void
  1635. fxFatal(const char* va_alist ...)
  1636. #define    fmt va_alist
  1637. {
  1638.     va_list ap;
  1639.     va_start(ap, va_alist);
  1640.     vsyslog(LOG_ERR, fmt, ap);
  1641.     va_end(ap);
  1642.     exit(-1);
  1643. }
  1644. #undef fmt
  1645.  
  1646. static    faxServerApp* app;
  1647.  
  1648. #include <signal.h>
  1649.  
  1650. static void
  1651. sigCleanup(int)
  1652. {
  1653.     if (app) {
  1654.     if (app->isRunning())
  1655.         app->close();
  1656.     delete app;
  1657.     }
  1658.     _exit(-1);
  1659. }
  1660.  
  1661. extern "C" int
  1662. main(int argc, char** argv)
  1663. {
  1664.     signal(SIGTERM, fxSIGHANDLER(sigCleanup));
  1665.     signal(SIGINT, fxSIGHANDLER(sigCleanup));
  1666.     app = new faxServerApp;
  1667.     app->initialize(argc, argv);
  1668.     app->open();
  1669.     while (app->isRunning())
  1670.     Dispatcher::instance().dispatch();
  1671.     app->close();
  1672.     delete app;
  1673.     return 0;
  1674. }
  1675.